package org.budo.graph.view.util;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.sql.DataSource;

import org.budo.support.javax.sql.util.JdbcUtil;
import org.budo.support.slf4j.Slf4j;
import org.budo.support.spring.context.aware.BudoApplicationContextAware;
import org.budo.support.spring.util.SpringUtil;
import org.slf4j.Logger;
import org.springframework.context.ApplicationContext;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;
import com.alibaba.druid.proxy.jdbc.ConnectionProxy;
import com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl;
import com.alibaba.druid.proxy.jdbc.DataSourceProxy;

/**
 * @author limingwei
 */
public class DruidDataSourceUtil {
    private static final Logger log = Slf4j.getLogger();

    private static List<DruidDataSource> _druidDataSourceList;

    public static List<DruidDataSource> getDruidDataSourceList() {
        if (null != _druidDataSourceList) {
            return _druidDataSourceList;
        }

        return _druidDataSourceList = findDruidDataSourceInApplicationContext();
    }

    private static List<DruidDataSource> findDruidDataSourceInApplicationContext() {
        BudoApplicationContextAware applicationContextAware = BudoApplicationContextAware.getInstance();
        if (null == applicationContextAware) {
            log.warn("#79 BudoApplicationContextAware is null");
            return null;
        }

        ApplicationContext applicationContext = applicationContextAware.getApplicationContext();
        if (null == applicationContext) {
            log.warn("#85 applicationContext is null, applicationContextAware=" + applicationContextAware);
            return null;
        }

        List<DruidDataSource> list = SpringUtil.getBeanListByType(applicationContext, DruidDataSource.class);
        if (null == list || list.isEmpty()) {
            log.debug("#91 druidDataSource is null, applicationContext=" + applicationContext); // 沒有 DruidDataSource
            return new ArrayList<DruidDataSource>();
        }

        return list;
    }

    public static Integer druidMaxActive(ApplicationContext applicationContext) {
        String[] beanNames = applicationContext.getBeanNamesForType(DruidDataSource.class);
        if (null == beanNames) {
            log.error("#56 druidMaxActive, applicationContext=" + applicationContext);
            return -1;
        }

        if (beanNames.length < 1) {
            log.error("#60 druidMaxActive, beanNames=" + Arrays.toString(beanNames) + ", applicationContext=" + applicationContext);
            return -2;
        }

        Integer maxActive = 0;

        for (String beanName : beanNames) {
            DruidDataSource druidDataSource = (DruidDataSource) applicationContext.getBean(beanName);
            maxActive += druidDataSource.getMaxActive();
        }

        return maxActive;
    }

    public static Integer druidActiveCount(ApplicationContext applicationContext) {
        if (null == applicationContext) {
            log.error("#68 druidActiveCount, applicationContext=" + applicationContext);
            return -3;
        }

        String[] beanNames = applicationContext.getBeanNamesForType(DruidDataSource.class);

        if (null == beanNames) {
            log.error("#75 druidActiveCount, beanNames=" + Arrays.toString(beanNames) + ", applicationContext=" + applicationContext);
            return -1;
        }

        if (beanNames.length < 1) {
            log.debug("#80 druidActiveCount, beanNames=" + Arrays.toString(beanNames) + ", applicationContext=" + applicationContext);
            return -2;
        }

        Integer activeCount = 0;

        for (String beanName : beanNames) {
            DruidDataSource druidDataSource = (DruidDataSource) applicationContext.getBean(beanName);
            activeCount += druidDataSource.getActiveCount();
        }

        return activeCount;
    }

    public static Boolean isMySQL(Connection connection) {
        if (null == connection) {
            log.error("#108 isMySQL return false, connection=" + connection);
            return false;
        }

        try {
            DatabaseMetaData metaData = connection.getMetaData();
            String databaseProductName = metaData.getDatabaseProductName();
            return "MySQL".equalsIgnoreCase(databaseProductName);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public static Boolean isOracle(Connection connection) {
        if (null == connection) {
            log.error("#125 isMySQL return false, connection=" + connection);
            return false;
        }

        try {
            DatabaseMetaData metaData = connection.getMetaData();
            String databaseProductName = metaData.getDatabaseProductName();

            return "Oracle".equalsIgnoreCase(databaseProductName);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public static String getSchema(Connection connection) {
        if (connection instanceof DruidPooledConnection) {
            DruidPooledConnection pooledConnection = (DruidPooledConnection) connection;
            Connection _connection = pooledConnection.getConnection();
            return getSchema(_connection);
        }

        try {
            return connection.getSchema();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public static String getUsername(Connection connection) {
        if (connection instanceof DruidPooledConnection) {
            DruidPooledConnection pooledConnection = (DruidPooledConnection) connection;
            Connection _connection = pooledConnection.getConnection();
            return getUsername(_connection);
        }

        if (connection instanceof ConnectionProxy) {
            ConnectionProxyImpl connectionProxy = (ConnectionProxyImpl) connection;
            DataSourceProxy dataSourceProxy = connectionProxy.getDirectDataSource();
            return getUsername(dataSourceProxy);
        }

        log.error("#170 connection.type=" + connection.getClass() + ", connection=" + connection);
        return null;
    }

    public static String getUrl(Connection connection) {
        if (connection instanceof DruidPooledConnection) {
            DruidPooledConnection pooledConnection = (DruidPooledConnection) connection;
            Connection _connection = pooledConnection.getConnection();
            return getUrl(_connection);
        }

        if (connection instanceof ConnectionProxy) {
            ConnectionProxyImpl connectionProxy = (ConnectionProxyImpl) connection;
            DataSourceProxy dataSourceProxy = connectionProxy.getDirectDataSource();
            return getUrl(dataSourceProxy);
        }

        log.error("#188 connection.type=" + connection.getClass() + ", connection=" + connection);
        return null;
    }

    public static String getUrl(DataSourceProxy dataSourceProxy) {
        if (dataSourceProxy instanceof DruidDataSource) {
            DruidDataSource druidDataSource = (DruidDataSource) dataSourceProxy;
            return druidDataSource.getUrl();
        }

        log.error("#198 dataSourceProxy.type=" + dataSourceProxy.getClass() + ", dataSourceProxy=" + dataSourceProxy);
        return null;
    }

    public static String getUsername(DataSourceProxy dataSourceProxy) {
        if (dataSourceProxy instanceof DruidDataSource) {
            DruidDataSource druidDataSource = (DruidDataSource) dataSourceProxy;
            return druidDataSource.getUsername();
        }

        log.error("#180 dataSourceProxy.type=" + dataSourceProxy.getClass() + ", dataSourceProxy=" + dataSourceProxy);
        return null;
    }

    public static Boolean isMySQL(DataSource dataSource) {
        if (null == dataSource) {
            log.error("#186 isMySQL return false, dataSource=" + dataSource);
            return false;
        }

        Connection connection = null;
        try {
            connection = JdbcUtil.getConnection(dataSource);
            return isMySQL(connection);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            JdbcUtil.close(connection);
        }
    }

    public static Boolean isOracle(DataSource dataSource) {
        if (null == dataSource) {
            log.error("#195 isOracle return false, dataSource=" + dataSource);
            return false;
        }

        Connection connection = null;
        try {
            connection = JdbcUtil.getConnection(dataSource);
            return isOracle(connection);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            JdbcUtil.close(connection);
        }
    }

    public static String getUsername(DataSource dataSource) {
        if (null == dataSource) {
            log.error("#204 getUsername return false, dataSource=" + dataSource);
            return null;
        }

        Connection connection = null;
        try {
            connection = JdbcUtil.getConnection(dataSource);
            return getUsername(connection);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            JdbcUtil.close(connection);
        }
    }

    public static String getUrl(DataSource dataSource) {
        if (null == dataSource) {
            log.error("#238 getUrl return false, dataSource=" + dataSource);
            return null;
        }

        Connection connection = null;
        try {
            connection = JdbcUtil.getConnection(dataSource);
            return getUrl(connection);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            JdbcUtil.close(connection);
        }
    }
}